/*---------------------------------------------------------------------------------

	Simple demonstration of sprites using textured quads

---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
#include <fat.h>
#include "gfxUtils.h"
#include "font.h"
#include "widgets.h"
extern "C" {
#include "sudoku.h"
}


//#include "ballsprites_bin.h"
#include "tiles_bin.h"
#include "Vera_bin.h"

typedef struct SudoCell
{
	s16 value;
	unsigned int state;
} SudoGrid[9][9];

// GIVEN
#define GIVEN		(1<<0)
#define INCORRECT	(1<<1)
#define UNCERTAIN	(1<<2)

SudoGrid curGrid;
int curX = 0;
int curY = 0;

#define THREADSTACK 512*1024	/*** Default 8k stack space ***/
//lwpq_t ThreadOneQ;
u8 threadonestack[THREADSTACK];

struct sudoku_info sudoku;

static void * threadone (void *arg)
{
	srand(time(NULL));
//  LWP_InitQueue (&ThreadOneQ);
	sud_struct_zero(&sudoku);
	fill(&sudoku);
	//~ display_grid(&sudoku);
//printf("thin\n");
	thin(&sudoku);
//	display_grid(&sudoku);
//	sleep(1);
  return NULL;
}

//void drawSpriteTex( int x, int y, int width, int height, int image, f32 angle );
void drawSudoGrid(SudoGrid grid);
void drawSudoCursor(int x, int y);
bool sudoCheck(SudoGrid grid);

void saveGame(char* filename)
{
	FILE* saveFile = fopen(filename, "wb");
	if(saveFile)
	{
		char buffer[81];
		int y;
		int i = 0;
		for(y=0;y<9;++y)
		{
			int x;
			for(x=0;x<9;++x)
			{
				buffer[i] = curGrid[y][x].value;
				buffer[i]|= curGrid[y][x].state&GIVEN?		0x80:0;
				buffer[i]|= curGrid[y][x].state&UNCERTAIN?	0x40:0;
				++i;
			}
		}
		fwrite(buffer, 1, 81, saveFile);
		fclose(saveFile);
	}
}

void resetGame()
{
	lwp_t ThreadOneT;
	memcpy(threadonestack+THREADSTACK-10, "canary", 6);
	/*** Create Thread ***/
	LWP_CreateThread (&ThreadOneT, threadone, NULL, threadonestack, THREADSTACK, 128);
	int y;
	if(memcmp(threadonestack+THREADSTACK-10, "canary", 6))
	{
		for(y=0;y<9;++y)
		{
			int x;
			for(x=0;x<9;++x)
			sudoku.grid[y][x]=9;
		}	
	}

	for(y=0;y<9;++y)
	{
		int x;
		for(x=0;x<9;++x)
		{
			curGrid[y][x].value = sudoku.grid[y][x];
			curGrid[y][x].state = sudoku.grid[y][x]==0?0:GIVEN;
		}
	}
}

char* testMenu[] = {"Save Game", "Reset Game", "Controls", "How to play", "About", "Snapshot", "Exit"};
char* exitMenu[] = {"Exit to Loader", "Power Off", "Cancel"};

char* controlTxt = {
"Game Mode\n\
------------------\n\
DPad  : cursor movement\n\
1/2     : change number\n\
A        : Toggle 'uncertain'\n\
home : Game menu\n\
-         : Save game\n\
+       : Screenshot (if activated)\n\
\n\
Menu Mode\n\
------------------\n\
DPad  : scroll text/change selection\n\
2        : select"};

char* aboutTxt = {
"Sudoku for Wii.\n\
Version  : 0.1b\n\
Coder    : dlkj\n\
Website : http://code.google.com/p/wiisudoku\n\
\n\
Accessories supported\n\
 - wiimote (1 player only, held sideways)\n\
 - sdcard (savegame and screenshot)\n\
\n\
libraries used\n\
 - libogc/libfat\n\
 - pngu/libpng\n\
 - freetype2\n\
\n\
tools used\n\
 - devkitPro\n\
 - GIMP\n\
\n\
fonts used\n\
 - Continuum\n\
 - Bitstream Vera"};

char* howTxt = {
"The objective is to completely fill in the sudoku grid.\n\
The catch is that no numbers may be repeated in the\n\
same column, row or 3x3 cell.\n\
Some numbers are 'given' to start off a game. These\n\
cannot be changed during gameplay.\n\
\n\
Cells with a dark background are 'given' cells\n\
Cells with red background are incorrect because they\n\
violate the 'no repeats' rule above.\n\
Cells can be marked 'uncertain' to help keep track of\n\
possibilities.\n\
Uncertain cells are tinted gray"};

//---------------------------------------------------------------------------------
int main( int argc, char **argv ){
//---------------------------------------------------------------------------------
	bool snapshotEnabled = false;
	VideoInit();
	WPAD_Init();
// 	WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR);
//	WPAD_SetVRes(WPAD_CHAN_ALL, rmode->fbWidth, rmode->xfbHeight);
	
	GraphicsInit();

	loadPngTiles( tiles_bin , 4, 4);

	FontInit();
	Font vera(Vera_bin, Vera_bin_size, 40);
	defaultFont = &vera;

	// initialize the wii SD card fat interface.
	fatInitDefault();
	fatSetDefaultInterface(PI_INTERNAL_SD);
	// now I can fopen, etc

	char savePath[50];
	getcwd(savePath, 50);
	strcat(savePath, "/save.bin");

	FILE* saveFile = fopen(savePath, "rb");
	if(saveFile)
	{
		char buffer[81];
		fread(buffer, 1, 81, saveFile);
		fclose(saveFile);
		saveFile = NULL;		
		int y;
		int i = 0;
		for(y=0;y<9;++y)
		{
			int x;
			for(x=0;x<9;++x)
			{
				curGrid[y][x].value = buffer[i]&0x3F;
				curGrid[y][x].state = buffer[i]&0x80?GIVEN:0;
				curGrid[y][x].state|= buffer[i]&0x40?UNCERTAIN:0;
				++i;
			}
		}
	}
	else
	{
		resetGame();
	}

	while(1) {

		WPAD_ScanPads();
//		WPAD_IR(0, &ir);
		u32 btns = WPAD_ButtonsDown(0);

		if (btns & WPAD_BUTTON_HOME)
		{
			switch(selectionMenu("Sudoku", testMenu, 7))
			{
			case 0: // save game
				if(messageBox("Save Game?", "No", "Yes"))
				{
					saveGame(savePath);
				}
				break;
			case 1:	//reset
				if(messageBox("Reset Game?", "No", "Yes"))
				{
					resetGame();
				}
				break;
			case 2:	// Controls
				infoBox("Controls", controlTxt);
				break;
			case 3:	// How to play
				infoBox("How to Play", howTxt);
				break;
			case 4:	// about
				infoBox("About", aboutTxt);
				break;
			case 5:	// snapshot
				snapshotEnabled = !snapshotEnabled;
				messageBox(snapshotEnabled?"Snapshot via + button":"Snapshot disabled", "OK");
				break;
			case 6:	// exit
				switch(selectionMenu("Exit", exitMenu, 3))
				{
				case 0:
//					VIDEO_SetBlack(TRUE);
//					WPAD_Shutdown();
					exit(0);
					break;
				case 1:
					SYS_ResetSystem(SYS_POWEROFF,0,0);
					break;
				}
				break;
			}
		}
		if(btns & WPAD_BUTTON_UP)
		{
			if(--curX==-1) curX=8;
		}
		if(btns & WPAD_BUTTON_DOWN)
		{
			if(++curX==9) curX=0;
		}
		if(btns & WPAD_BUTTON_LEFT)
		{
			if(++curY==9) curY=0;
		}
		if(btns & WPAD_BUTTON_RIGHT)
		{
			if(--curY==-1) curY=8;
		}
		if(btns & WPAD_BUTTON_1)
		{
			if( !(curGrid[curY][curX].state&GIVEN) && (--curGrid[curY][curX].value==-1) ) curGrid[curY][curX].value=9;
		}
		if(btns & WPAD_BUTTON_2)
		{
			if( !(curGrid[curY][curX].state&GIVEN) && (++curGrid[curY][curX].value==10) ) curGrid[curY][curX].value=0;
		}
		if(btns & WPAD_BUTTON_MINUS && messageBox("Save Game?", "No", "Yes"))
		{
			saveGame(savePath);
		}
		if(btns & WPAD_BUTTON_A)
		{
			curGrid[curY][curX].state ^= UNCERTAIN;
		}
		if(btns & WPAD_BUTTON_PLUS && snapshotEnabled)
		{
			screenShot("/sudoku_snap.png");
			messageBox("screenshot saved in sd card", "OK");
		}

		if(sudoCheck(curGrid))
		{
			messageBox("YOU WIN!!!", "OK");
			if(messageBox("Reset Game?", "No", "Yes"))
			{
				resetGame();
			}
		}
		
		vera.drawText(320-vera.getTextWidth("Sudoku 0.1b", 30)/2 , 20,"Sudoku 0.1b", 30);
		drawSudoGrid(curGrid);
		drawSudoCursor(curX,curY);

		renderToFrameBuffer(getSpareFrameBuffer());
		swapFrameBuffers();
		waitVSync();
	}
	return 0;
}

bool sudoCheck(SudoGrid grid)
{
	int y, x;
	bool full = true;
	bool incorrect = false;
	// clear invalid status.. check again
	for(y=0; y<9;++y)
	for(x=0;x<9;++x)
	{
		grid[y][x].state&=~INCORRECT;
		if(grid[y][x].value==0)	full = false;
	}
	// check duplicates in rows
	for(y=0; y<9;++y)
	for(x=0; x<8;++x)
	{
		if(grid[y][x].value!=0)
		{
			int x2;
			for(x2=x+1;x2<9;++x2)
			{
				if(grid[y][x].value==grid[y][x2].value)
				{
					grid[y][x ].state|=INCORRECT;
					grid[y][x2].state|=INCORRECT;
					incorrect = true;
				}
			}
		}
	}

	// check duplicates in columns
	for(x=0; x<9;++x)
	for(y=0;y<8;++y)
	{
		if(grid[y][x].value!=0)
		{
			int y2;
			for(y2=y+1;y2<9;++y2)
			{
				if(grid[y][x].value==grid[y2][x].value)
				{
					grid[y ][x].state|=INCORRECT;
					grid[y2][x].state|=INCORRECT;
					incorrect = true;
				}
			}
		}
	}

	for(y=0;y<9;y+=3)
	for(x=0;x<9;x+=3)
	{
		// check 3x3 block duplicates
		int x2,y2;
		for(y2=0; y2<3; ++y2)
		for(x2=0; x2<3; ++x2)
		{
			if(grid[y+y2][x+x2].value!=0)
			{
				int x3=x2;
				int y3=y2;
				for(;y3<3;y3++)
				{
					for(;x3<3;x3++)
					{
						if( y2!=y3 || x2!=x3)
						if(grid[y+y2][x+x2].value==grid[y+y3][x+x3].value)
						{
							grid[y+y2][x+x2].state|=INCORRECT;
							grid[y+y3][x+x3].state|=INCORRECT;
							incorrect = true;
						}
					}
					x3 = 0;
				}
				
			}
		}
	}
	return (full && incorrect==false);
}

void drawSudoGrid(SudoGrid grid)
{
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define TILE_WIDTH  32
#define TILE_HEIGHT 32
#define INTER_SPACE 5
	int yPos = (SCREEN_HEIGHT-9*TILE_HEIGHT-2*INTER_SPACE)/2;
	int y;
	for(y=0; y<9;++y)
	{
		int xPos = (SCREEN_WIDTH-9*TILE_WIDTH-2*INTER_SPACE)/2;
		if(y==3||y==6)
		{
			yPos+=INTER_SPACE;
		}
		int x;
		for(x=0;x<9;++x)
		{
			if(x==3||x==6)
			{
				xPos+=INTER_SPACE;
			}
			// draw background tile first
			int tile;
// = (grid[y][x].state&GIVEN)?0x0C:(grid[y][x].state&INCORRECT)?0x0D:0x0A;
			if(grid[y][x].state&GIVEN)
			{
				tile = 0x0C;
			}
			else
			{
				tile = (grid[y][x].state&INCORRECT)?0x0D:0x0A;
				if(grid[y][x].state&UNCERTAIN)
					tile++;
			}
			drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, tile, 0);
			if(grid[y][x].value!=0)
			{
				drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, grid[y][x].value, 0);
			}
			xPos+=TILE_WIDTH;
		}
		yPos+=TILE_HEIGHT;
	}
}

void drawSudoCursor(int x, int y)
{
	int yPos = (SCREEN_HEIGHT-9*TILE_HEIGHT-2*INTER_SPACE)/2;
	int xPos = (SCREEN_WIDTH-9*TILE_WIDTH-2*INTER_SPACE)/2;
	xPos+=x*TILE_WIDTH;
	if(x>=3) xPos+=INTER_SPACE;
	if(x>=6) xPos+=INTER_SPACE;
	yPos+=y*TILE_HEIGHT;
	if(y>=3) yPos+=INTER_SPACE;
	if(y>=6) yPos+=INTER_SPACE;
	drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, 0x0F, 0);
	
}
